using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Threading;

namespace OpenWaves.Caching
{
    public class MonitorBasedLockManager : ILockManager
    {
        private readonly ConcurrentDictionary<string, MonitorLock> locks = new ConcurrentDictionary<string,MonitorLock>();

        public bool TryAcquireLock(string key, TimeSpan timeout)
        {
            var monitorLock = this.locks.GetOrAdd(key, new MonitorLock());

            lock(monitorLock)
            {
                while (monitorLock.Taken)
                {
                    Debug.WriteLine("{0}: Before wait", Thread.CurrentThread.ManagedThreadId);
                    if (Monitor.Wait(monitorLock, timeout == TimeSpan.MaxValue ? TimeSpan.FromMilliseconds(-1) : timeout) == false)
                    {
                        Debug.WriteLine("{0}: Wait returned false", Thread.CurrentThread.ManagedThreadId);
                        return false;
                    }
                    Debug.WriteLine("{0}: Wait returned true", Thread.CurrentThread.ManagedThreadId);
                }

                monitorLock.Taken = true;
                this.locks.TryAdd(key, monitorLock);
                return true;
            }
        }

        public void ReleaseLock(string key)
        {
            MonitorLock monitorLock;
            var monitorLockFound = this.locks.TryGetValue(key, out monitorLock);
            Contract.Assert(monitorLockFound, "Cannot release a lock that was not previously acquired.");

            lock (monitorLock)
            {
                monitorLock.Taken = false;
                this.locks.TryRemove(key, out monitorLock);
                Monitor.Pulse(monitorLock);
            }
        }

        private class MonitorLock
        {
            public bool Taken;
        }
    }
}